﻿using System;
using System.Collections.Generic;
using System.Text;
using SH;

namespace MTS
{
    class mtVHost
    {
        bool m_bActive = false;
        bool m_bInit = false;
        readonly object m_mtx = new object();

        readonly object m_mtxStub = new object();

        MTSDLLWrap _mts ;

         mtInterfaceManager m_pInterfaceManager;

        Dictionary<string, mtStub> m_mapStub = new Dictionary<string, mtStub>();

        uint m_nRefCount = 0;

        ulong handle = 0;

        mtRunEnv m_runEnv;

        degtOnCallbackOneWay m_pfnCallbackOneWay;
        degtOnCallbackTwoWay m_pfnCallbackTwoWay;
        degtOnStationInfoNotify m_pfnStationInfoNotify;

      //  degtOnDelegate m_pfnDelegate;

        public emProgramType ProgramType
        {
            get => m_runEnv.ProgramType;
        }

        public mtStub GetStub(string token)
        {
            mtStub stub;
            lock (m_mtxStub)
            {
                if(m_mapStub.TryGetValue(token, out stub))
                {
                    return stub;
                }
                else
                {
                    return null;
                }
            }
        }

        public ulong Handle { get => handle; }

        public mtVHost(ulong handle, mtRegisterInfo info, bool bAutoLogon)
        {
            this.handle = handle;

            _mts = MTSDLLWrap.getInstance();

            m_runEnv = new mtRunEnv(info, bAutoLogon);
        }

        public shErrorCode Initialize()
        {
            if (m_bInit == false)
            {
                m_pInterfaceManager = new mtInterfaceManager(this);

                var hRet = m_pInterfaceManager.Initialize(m_runEnv.RegisterInfo);

                if (hRet != shErrorCode.sh_ec_Success)
                    return hRet;

                var IvkParam = mtCHelptr.MakeInvokeParam( 0, 1);
                mtCDataParam dataParam = mtCHelptr.MakeEmptyDataParam();

                var ec = _mts.mtInvokeOneWay(handle, ref IvkParam, ref dataParam);

                if (ec != 0)
                {
                    return (shErrorCode)ec;
                }

                m_pfnCallbackOneWay = new degtOnCallbackOneWay(OnCallbackOneWay);
                m_pfnCallbackTwoWay = new degtOnCallbackTwoWay(OnCallbackTwoWay);
                m_pfnStationInfoNotify = new degtOnStationInfoNotify(OnStationInfoNotify);

                ec = _mts.mtSetCallback(handle, m_pfnCallbackOneWay, m_pfnCallbackTwoWay, m_pfnStationInfoNotify);

                if (ec != 0)
                    return (shErrorCode)ec;

            }

            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode Start()
        {

            if (m_bActive == false)
            {
                var hRet = m_pInterfaceManager.Start();

                if (hRet != shErrorCode.sh_ec_Success)
                    return hRet;

                var IvkParam = mtCHelptr.MakeInvokeParam( 0, 2);
                mtCDataParam dataParam = mtCHelptr.MakeEmptyDataParam();

                var ec = _mts.mtInvokeOneWay(handle,ref IvkParam,ref dataParam);

                if (ec != 0)
                {
                    m_pInterfaceManager.Stop();

                    return (shErrorCode)ec;
                }
                  

                m_bActive = true;
            }

            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode Stop()
        {
            if (m_bActive == true)
            {
                var hRet = m_pInterfaceManager.Stop();

                var IvkParam = mtCHelptr.MakeInvokeParam( 0, 3);
                mtCDataParam dataParam = mtCHelptr.MakeEmptyDataParam();

                _mts.mtInvokeOneWay(handle, ref IvkParam, ref dataParam);

                m_bActive = false;
            }

            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode LogOn( string userName, string password)
            {
            var IvkParam = mtCHelptr.MakeInvokeParam(0, 5);

            MTSSerializer sc = new MTSSerializer();

            sc.serialize(ref userName);
            sc.serialize(ref password);


            mtCDataParam dataParam = mtCHelptr.MakeDataParam(sc);

            var ec = _mts.mtInvokeOneWay(handle, ref IvkParam, ref dataParam);

            return (shErrorCode)ec;

        }

        public shErrorCode LogOut()
        {
            var IvkParam = mtCHelptr.MakeInvokeParam(0, 6);

            mtCDataParam dataParam = mtCHelptr.MakeEmptyDataParam();

            var ec = _mts.mtInvokeOneWay(handle, ref IvkParam, ref dataParam);

            return (shErrorCode)ec;

        }
        public mtRunEnv RunEnv { get => m_runEnv; }
        public uint IncCount()
        {
            return ++m_nRefCount;
        }

        public uint DecCount()
        {
            return --m_nRefCount;
        }

        public uint RefCount()
        {
            return m_nRefCount;
        }

        public mtRegisterInfo GetRegisterInfo()
        {
            return m_runEnv.RegisterInfo;
        }

        public mtInterfaceManager GetInterfaceManager()
        {
            return m_pInterfaceManager;
        }

        public mtInterface QueryInterface(string interfaceName)
        {
            return m_pInterfaceManager.QueryInterface(interfaceName);
        }

        public shErrorCode OnTestDelegate(ulong handle, ref mtCStationInfo info)
        {
            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode BindStub(mtCredential crd,mtLogOnInfo logOninfo, mtStationInfo stationInfo)
        {
            mtStub stub;

            lock (m_mtxStub)
            {
                if (m_mapStub.TryGetValue(crd.Token, out stub) == true)
                {
                    stub.LogOnInfo = logOninfo;
                    stub.Credential = crd;
 
                }
                else
                {
                    stub = new mtStub(logOninfo.ProgramType);

                    stub.LogOnInfo = logOninfo;
                    stub.Credential = crd;
    
                    m_mapStub.Add(crd.Token, stub);
                }
            }

            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode UnBindStub(string token, out mtLogOnInfo info, out mtCredential crd)
        {
            mtStub stub;
            lock (m_mtxStub)
            {
                if (m_mapStub.TryGetValue(token, out stub) == true)
                {
                    info = stub.LogOnInfo ;
                    crd  = stub.Credential;
                    m_mapStub.Remove(token);
                }
                else
                {
                    info = null;
                    crd = null; 
                }
            }

            return shErrorCode.sh_ec_Success;
        }

        public shErrorCode OnCallbackOneWay(ulong handle, ref mtCCallabckParam pParam, ref mtCDataParam pData)
        {
            //if (handle != this.handle)
            //{
            //    return shErrorCode.sh_ec_AccessDenied;
            //}

            if (pParam.nClassID == 0)
            {
                switch (pParam.nFuncID)
                {
                    case 1:
                    {
                       var buf = mtCHelptr.DataParamToBytes(pData);

                        MTSSerializer sc = new MTSSerializer(buf);

                            mtCredential crd = new mtCredential() ;
                            sc.serialize(crd);

                            m_runEnv.Credential = crd;

                            var pCallback = m_pInterfaceManager.GetClientOuterface<mtSysManageCallback>();

                            if (pCallback != null)
                            {
                                var pRawCallback = (ImtSysManageCallback)pCallback.GetCallback();

                                if (pRawCallback == null)
                                {
                                    return shErrorCode.sh_ec_NotActive;
                                }

                                pRawCallback.OnUpdateCredential(crd);

                            }

                            return shErrorCode.sh_ec_Success;
                    }
                    case 2:
                    {
                        m_runEnv.ClearCredential();

                        return shErrorCode.sh_ec_Success;
                    }
                    case 3:
                        {
                            var buf = mtCHelptr.DataParamToBytes(pData);

                            MTSSerializer sc = new MTSSerializer(buf);

                            mtStationInfo stationInfo = new mtStationInfo();
                            mtCredential crd = new mtCredential();
                            mtLogOnInfo info = new mtLogOnInfo();

                            sc.serialize(stationInfo);
                            sc.serialize(crd);
                            sc.serialize(info);

                            BindStub(crd,info,stationInfo);

                            var pCallback = m_pInterfaceManager.GetServerOuterface<mtSysManageServerCallback>();

                            if (pCallback != null)
                            {
                                var pRawCallback = (ImtSysManageServerCallback)pCallback.GetCallback();

                                if (pRawCallback == null)
                                {
                                    return shErrorCode.sh_ec_NotActive;

                                }

                                pRawCallback.OnRemoteStationUpdateCredential(stationInfo, crd);

                            }

                            return shErrorCode.sh_ec_Success;
                        }
            
                    }

                return shErrorCode.sh_ec_NoInterface;

            }
            else
            {
                var pOutface = m_pInterfaceManager.GetServerOuterface(pParam.nClassID);

                if (pOutface == null)
                {
                    pOutface = m_pInterfaceManager.GetClientOuterface(pParam.nClassID);
                }

                if (pOutface != null)
                {
                    var buf = mtCHelptr.DataParamToBytes(pData);

                    MTSSerializer sc = new MTSSerializer(buf);

                    return pOutface._OnInvokeOneWay(pParam.nClassID, pParam.nFuncID, pParam.token, sc);
                }
                else
                {
                    return shErrorCode.sh_ec_NoInterface;
                }
            }


        }

        public shErrorCode OnCallbackTwoWay(ulong handle, ref mtCCallabckParam pParam, ref mtCDataParam dataIn, ref mtCDataParam dataOut)
        {
            //if (handle != this.handle)
            //{
            //    return shErrorCode.sh_ec_AccessDenied;
            //}

            var pOutface = m_pInterfaceManager.GetServerOuterface(pParam.nClassID);

            if(pOutface == null)
            {
                pOutface = m_pInterfaceManager.GetClientOuterface(pParam.nClassID);
            }

            if (pOutface != null)
            {
                var buf = mtCHelptr.DataParamToBytes(dataIn);

                MTSSerializer sc = new MTSSerializer(buf);

                var ec = pOutface._OnInvokeTwoWay(pParam.nClassID, pParam.nFuncID, pParam.token,ref sc);

                if (ec != shErrorCode.sh_ec_Success)
                {
                    dataOut = mtCHelptr.MakeEmptyDataParam();

                }
                else
                {
                    dataOut = mtCHelptr.MakeDataOutParam(sc);
                 
                }

                return ec;
            }
            else
            {
                return shErrorCode.sh_ec_NoInterface;
            }
        }

        public shErrorCode OnStationInfoNotify(ulong handle, ref mtCStationInfo cInfo, int nFlag)
        {
            //if (handle != this.handle)
            //{
            //    return shErrorCode.sh_ec_AccessDenied;
            //}

            mtStationInfo info = new mtStationInfo(cInfo.name, cInfo.location, cInfo.guid, (emStationType)cInfo.stype, (emProgramType)cInfo.ptype, cInfo.bConnect == 1 ? true : false);

            if (mtTool.IsServer(ProgramType)) //server
            {
                var pCallback = m_pInterfaceManager.GetServerOuterface<mtSysManageServerCallback>();

                if (pCallback != null)
                {
                    var pRawCallback = (ImtSysManageServerCallback)pCallback.GetCallback();

                    if (pRawCallback == null)
                    {
                        return shErrorCode.sh_ec_NotActive;
                    }

                    if (nFlag == 1)
                    {
                        pRawCallback.OnRemoteStationOnline(info);

                    }
                    else if (nFlag == 2)
                    {

                        pRawCallback.OnRemoteStationOffline(info);

                    }
                    else if (nFlag == 3)
                    {

                        pRawCallback.OnRemoteStationConnected(info);

                    }
                    else if (nFlag == 4)
                    {
                        lock (m_mtxStub)
                        {
                            m_mapStub.Remove(cInfo.token);
                        }

                        pRawCallback.OnRemoteStationDisConnected(info);

                    }
                }
            }
            else // client
            {
                var pCallback = m_pInterfaceManager.GetClientOuterface<mtSysManageCallback>();

                if (pCallback != null)
                {
                    var pRawCallback = (ImtSysManageCallback)pCallback.GetCallback();

                    if (pRawCallback == null)
                    {
                        return shErrorCode.sh_ec_NotActive;

                    }

                    if (nFlag == 1)
                    {
                        pRawCallback.OnRemoteStationOnline(info);

                    }
                    else if (nFlag == 2)
                    {

                        pRawCallback.OnRemoteStationOffline(info);

                    }
                    else if (nFlag == 3)
                    {

                        pRawCallback.OnRemoteStationConnected(info);

                    }
                    else if (nFlag == 4)
                    {

                        pRawCallback.OnRemoteStationDisConnected(info);

                    }
                }

            }

            return shErrorCode.sh_ec_Success;
        }
    }
}
